﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using BLToolkit.Data;
using BLToolkit.TypeBuilder;
using Devsoft.Utilities.Extentions;

namespace Devsoft.DB.Models
{
    public class DBRouter
    {
        #region Constructors

        static DBRouter()
        {
            Default = new DBRouter();

        }

        protected DBRouter()
        {
            ConnectionStringName = "DBConnection";
            _configurations = new Dictionary<string, Configuration>();
        }

        #endregion


        #region Public Methods

        public static void Register(DBRouter router)
        {
            Default = router;
        }


        public virtual string GetConnectionName(Type type)
        {
            return ConnectionStringName;
        }


        public virtual DbManager GetDB(string connectionName)
        {
            var db = new DbManager(connectionName);
            db.BeforeOperation += db_BeforeOperation;
            return db;
            //return new DbManager(connectionName);
        }

        void db_BeforeOperation(object sender, OperationTypeEventArgs ea)
        {
            var db = sender as DbManager;
            Configuration configuration;
            if (ea.Operation.IsIn(OperationType.ExecuteNonQuery, OperationType.ExecuteReader, OperationType.ExecuteScalar) && db != null && db.Command != null && _configurations.TryGetValue(db.ConfigurationString, out configuration))
            {
                db.Command.CommandTimeout = configuration.CommandTimeout;
            }
        }

        public virtual DbManager GetDB(Type type)
        {
            return GetDB(GetConnectionName(type));
        }
        
        public virtual void Set(Configuration configuration)
        {
            Set(ConnectionStringName, configuration);
        }

        public virtual void Set(string connectionString,Configuration configuration)
        {
            _configurations[connectionString] = configuration;
        }

        public void Clear()
        {
            IsDirty = false;
        }


        public virtual void DisposeCurrentDB()
        {
            var db = CurrentDataContext;
            db.Dispose();
        }

        #endregion


        #region Properties

        public static DBRouter Default { get; set; }


        public string ConnectionStringName
        {
            get { return _connectionStringName; }
            set
            {
                IsDirty = (_connectionStringName != value);
                _connectionStringName = value;
            }
        }

        public bool IsDirty { get; protected set; }

        public DbManager GetDB<T>()
        {
            return DataContextFactory.GetScopedDataContext<DbManager>((Type) typeof(T));
        }

        public Manager<T> GetManager<T>()where T:class 
        {
            //return Activator.CreateInstance(typeof(TManager), DataContextFactory.GetScopedDataContext<DbManager>()) as TManager;
            //not good for obfuscation
            
            var bindings = BindingFlags.Static | BindingFlags.Public;
            var t2 = typeof (Manager<>);
            var expr = typeof (T).GetProperty("Objects",bindings);
            if (expr != null)
            {
                return expr.GetValue(null, new object[0]) as Manager<T>;    
            }
            else
            {
                var t = TypeFactory.GetType(typeof(Manager<T>));
                return Activator.CreateInstance(t, DataContextFactory.GetScopedDataContext<DbManager>(typeof(T))) as Manager<T>;    
            }
        }

        public DbManager CurrentDataContext { get { return DataContextFactory.GetScopedDataContext<DbManager>((Type) null); } }

        private string _connectionStringName;
        private Dictionary<string, Configuration> _configurations;

        #endregion

    }
}
